/**************************************************************************************
*
*	Copyright (C) 2009 Brent Andrew Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Pulse.h
*
*	Description -	Contains the base definitions and preprocessor settings.
*
*	Comments	-	
*
*	Modification History:
*			Name			   Date					Description
*			CodeSushi	-	10/18/2009		-	File creation
*			CodeSushi	-	10/22/2009		-	Fast asm memsets
*			CodeSushi	-	10/28/2009		-	Added strlen function.
**************************************************************************************/

#ifndef _PULSE_BASE_H_
#define _PULSE_BASE_H_

#include <memory.h>
#include <assert.h>
#include <cstring>
#include <stdlib.h>

// Platform specific data types
#include "PulseConfig.h"
#include "Platform.h"
#include "Exception.h"

//#include "PTLBase.h"

namespace Pulse
{
	/* Utility Macros here and Fast Functions Here */

	#if defined( PSX_UNICODE )
		#define PSX_String( str ) L##str
	#else
		#define PSX_String( str ) str
	#endif

	#define PSX_IsByteAligned( p, align ) ( ( ( (U32)p ) & ( align - 1 ) ) == 0 )
	#define PSX_AlignMemory( addr, align ) ( ( (U32)addr % align == 0 ) ? addr : ((U8*)addr) + ( align - ( (U32)addr % align ) ) )

	#if PSX_DEBUG // When we develop a more sophisticated debug manager, we should move this in its own place
		#define PSX_Assert( _exp, _msg ) assert( (_exp) && PSX_String(_msg) )
	#else
		#define PSX_Assert( _exp, _msg ) 
	#endif

	// The original exception code was causing some weird fatal errors.
	// This has been changed to simply show a message box.
	#define PSX_ThrowAssert( _exp, _msg )	\
		if ( (##_exp##) == false )	{ ::MessageBox( NULL, _msg, "Fatal Error!", MB_OK );; } 

	#define PSX_Delete( _ptr ) if ( _ptr ) delete _ptr; _ptr = NULL;
	#define PSX_DeleteArray( _ptr ) if ( _ptr ) delete [] _ptr; _ptr = NULL;

	// Generic memory functions
	#if defined(PSX_PLATFORM_WINDOWS)
		#define PSXMallocAligned( _size, _alignment, _type ) (##_type##*)_aligned_malloc( _size, _alignment )
		#define PSXDeallocAligned( _ptr ) _aligned_free( (VOID*)_ptr )
		#define PSXMalloc( _size, _type ) (##type##*)malloc( _size )
		#define PSXDealloc( _ptr ) free( (VOID*)##_ptr )
	#else
		#error Non-windows platform. Malloc and dealloc not implemented
	#endif

	PSX_FORCE_INLINE VOID PSX_MemSetQuad(VOID *dest, UINT data, U32 count)
	{
		// this function fills or sets unsigned 32-bit aligned memory
		// count is number of quads
		_asm
		{
			mov edi, dest;	edi points to destination memory
			mov ecx, count; number of 32-bit words to move
			mov eax, data;	32-bit data
			rep stosd;		move data
		} 

	}

	PSX_FORCE_INLINE VOID PSX_MemSetWord(VOID *dest, USHORT data, U32 count)
	{
		// this function fills or sets unsigned 16-bit aligned memory
		// count is number of words
		_asm
		{
			mov edi, dest;	edi points to destination memory
			mov ecx, count; number of 16-bit words to move
			mov ax, data;	16-bit data
			rep stosw;		move data
		}
	}

	#define PSX_MemSet( _dest, _val, _size ) memset( _dest, _val, _size )
	#define PSX_ZeroMem( _dest, _size ) ZeroMemory( _dest, _size )

	#ifdef PSX_PLATFORM_WINDOWS
		PSX_FORCE_INLINE __declspec(align(16)) UINT __stdcall PSX_StrLen(const CHAR* pStr)
		{
			PSX_Assert( pStr, "pStr string is NULL." );

			__declspec(align(16)) int result = 0;
			__asm
			{
				xor eax, eax
					mov edi, [pStr]
				mov ecx, eax
					dec ecx
					repne scasb
					not ecx
					dec ecx
					mov [result], ecx
			}
			return result;
		}
	#else
		PSX_INLINE UINT PSX_StrLen( const CHAR *pStr )
		{
			PSX_Assert( pStr, "pStr string is NULL." );

			/*
			// By Peter - Fast StrLen version
			// NOTE: This is Unsafe. No check for access violation at end of memory address
			UINT len = 0;
			UINT x;
			for(;;) {
				x = *(unsigned*)s;
				if((x & 0xFF) == 0) return len;
				if((x & 0xFF00) == 0) return len + 1;
				if((x & 0xFF0000) == 0) return len + 2;
				if((x & 0xFF000000) == 0) return len + 3;
				s += 4, len += 4;
			}
			*/

			UINT i = 0;
			while ( pStr )
			{
				++i;
				++pStr;
			};

			return i;
		}
	#endif

	PSX_FORCE_INLINE INT PSX_StrCmp( CONST CHAR *pStr1, CONST CHAR *pStr2 )
	{
		return (*pStr1 != *pStr2 ? *pStr1 - *pStr2 : strcmp( pStr1, pStr2 ) );
	}

	#if defined( PSX_UNICODE )
		#define PSX_MemCopy( _dest, _source, _count ) wmemcpy( _dest, _source, _count )
	#else
		#define PSX_MemCopy( _dest, _source, _count ) memcpy( _dest, _source, _count )
	#endif

	#define PSX_Get16Bit( d ) ((((U32)(((const U8 *)(d))[1])) << 8) + (U32)(((const U8 *)(d))[0]) )

	PSX_INLINE INT PSX_Rand( void )
	{
		return rand();
	}

	PSX_INLINE INT PSX_Rand( INT min, INT max )
	{
		return rand() % ( ( max + 1 ) - min ) + min;
	}

	UINT PSX_SuperFastHash( CONST CHAR *pData, UINT len );

	template < typename T >
	PSX_INLINE UINT PSX_SuperFastHash( const T &obj )
	{ return PSX_SuperFastHash( (const char*)&obj, sizeof( T ) ); }

	template < typename T >
	PSX_INLINE UINT PSX_SuperFastHash( const T *obj )
	{ return PSX_SuperFastHash( (const char*)obj, sizeof( T ) ); }

	UINT PSX_NextPrime( UINT n );

	BOOL PSX_IsPrime( UINT n );

	// TODO: Move these to a math header or something...
	#define PSX_PI 3.141592654f
	#define PSX_DegToRad( _deg ) ( _deg * (PSX_PI / 180.0f) )
	#define PSX_RadToDeg( _rad ) ( _rad * (180.0f / PSX_PI) )

};


#endif /* _PULSE_BASE_H_ */